﻿///////////////////////////////////////////////////////////////////////////////////////////////////////
// Copyright (c) 2023, ООО 1С-Софт
// Все права защищены. Эта программа и сопроводительные материалы предоставляются 
// в соответствии с условиями лицензии Attribution 4.0 International (CC BY 4.0)
// Текст лицензии доступен по ссылке:
// https://creativecommons.org/licenses/by/4.0/legalcode
///////////////////////////////////////////////////////////////////////////////////////////////////////

#Если Сервер Или ТолстыйКлиентОбычноеПриложение Или ВнешнееСоединение Тогда

#Область ПрограммныйИнтерфейс

#Область ДляВызоваИзДругихПодсистем

// СтандартныеПодсистемы.ВариантыОтчетов

// Параметры:
//   Настройки - см. ВариантыОтчетовПереопределяемый.НастроитьВариантыОтчетов.Настройки.
//   НастройкиОтчета - см. ВариантыОтчетов.ОписаниеОтчета.
//
Процедура НастроитьВариантыОтчета(Настройки, НастройкиОтчета) Экспорт
	НастройкиОтчета.ОпределитьНастройкиФормы = Истина;
	
	МодульВариантыОтчетов = ОбщегоНазначения.ОбщийМодуль("ВариантыОтчетов");
	МодульВариантыОтчетов.УстановитьРежимВыводаВПанеляхОтчетов(Настройки, НастройкиОтчета, Ложь);
	
	НастройкиВарианта = МодульВариантыОтчетов.ОписаниеВарианта(Настройки, НастройкиОтчета, "АнализАктивностиПользователей");
	НастройкиВарианта.Описание = 
		НСтр("ru = 'Позволяет выполнять мониторинг активности пользователей
		|в программе (насколько интенсивно и с какими объектами работают пользователи).'");
	
	НастройкиВарианта = МодульВариантыОтчетов.ОписаниеВарианта(Настройки, НастройкиОтчета, "АктивностьПользователя");
	НастройкиВарианта.Описание = 
		НСтр("ru = 'Подробная информация о том,
		|с какими объектами работал пользователь в программе.'");
	
	НастройкиВарианта = МодульВариантыОтчетов.ОписаниеВарианта(Настройки, НастройкиОтчета, "КонтрольЖурналаРегистрации");
	НастройкиВарианта.Описание = НСтр("ru = 'Список критичных записей журнала регистрации.'");
	НастройкиВарианта.НастройкиДляПоиска.ИменаМакетов = "МакетОтчетаПоОшибкамВЖурналеРегистрации";
	
	НастройкиВарианта = МодульВариантыОтчетов.ОписаниеВарианта(Настройки, НастройкиОтчета, "ПродолжительностьРаботыРегламентныхЗаданий");
	НастройкиВарианта.Описание = НСтр("ru = 'Выводит график выполнения регламентных заданий в программе.'");
	НастройкиВарианта.НастройкиДляПоиска.ИменаМакетов = "ПродолжительностьРаботыРегламентныхЗаданий, РасшифровкаРегламентныхЗаданий";
КонецПроцедуры

// Конец СтандартныеПодсистемы.ВариантыОтчетов

#КонецОбласти

#КонецОбласти

#Область СлужебныеПроцедурыИФункции

// Функция получает информацию по активности пользователей из журнала регистрации
// по переданному периоду.
//
// Параметры:
//    ПараметрыОтчета - Структура:
//    * ДатаНачала          - Дата   - начало периода, по которому будет собираться информация.
//    * ДатаОкончания       - Дата   - окончание периода, по которому будет собираться информация.
//    * Пользователь        - Строка - имя пользователя, по которому проводить анализ.
//                                     Для варианта отчета "Активность пользователя".
//    * ПользователиИГруппы - СписокЗначений - где значение - группа(ы) пользователей и(или)
//                                     пользователь(и), по которым проводить анализ.
//                                     Для варианта отчета "Анализ активности пользователей".
//    * ВариантОтчета       - Строка - "АктивностьПользователя" или "АнализАктивностиПользователей".
//    * ВыводитьЗадачи      - Булево - получать или нет информацию по задачам из журнала регистрации.
//    * ВыводитьСправочники - Булево - получать или нет информацию по справочникам из журнала регистрации.
//    * ВыводитьДокументы   - Булево - получать или нет информацию документов из журнала регистрации.
//    * ВыводитьБизнесПроцессы - Булево - получать или нет информацию по бизнес процессам из журнала регистрации.
//
// Возвращаемое значение:
//  ТаблицаЗначений - таблица, содержащая не сгруппированную информацию по активности
//     пользователей из журнала регистрации.
//
Функция ДанныеИзЖурналаРегистрации(ПараметрыОтчета) Экспорт
	
	// Подготовка параметров отчета.
	ДатаНачала = ПараметрыОтчета.ДатаНачала;
	ДатаОкончания = ПараметрыОтчета.ДатаОкончания;
	Пользователь = ПараметрыОтчета.Пользователь;
	ПользователиИГруппы = ПараметрыОтчета.ПользователиИГруппы;
	ВариантОтчета = ПараметрыОтчета.ВариантОтчета;
	
	Если ВариантОтчета = "АктивностьПользователя" Тогда
		ВыводитьБизнесПроцессы = ПараметрыОтчета.ВыводитьБизнесПроцессы;
		ВыводитьЗадачи = ПараметрыОтчета.ВыводитьЗадачи;
		ВыводитьСправочники = ПараметрыОтчета.ВыводитьСправочники;
		ВыводитьДокументы = ПараметрыОтчета.ВыводитьДокументы;
	Иначе
		ВыводитьСправочники = Истина;
		ВыводитьДокументы = Истина;
		ВыводитьБизнесПроцессы = Ложь;
		ВыводитьЗадачи = Ложь;
	КонецЕсли;
	
	// Формируем таблицу исходных данных.
	ИсходныеДанные = Новый ТаблицаЗначений();
	ИсходныеДанные.Колонки.Добавить("Дата", Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.Дата)));
	ИсходныеДанные.Колонки.Добавить("Неделя", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(10)));
	ИсходныеДанные.Колонки.Добавить("Пользователь");
	ИсходныеДанные.Колонки.Добавить("ЧасовРаботы", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(15,2)));
	ИсходныеДанные.Колонки.Добавить("КоличествоЗапусков", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("СозданоДокументов", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("СозданоСправочников", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("ИзмененоДокументов", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("СозданоБизнесПроцессов",	Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("СозданоЗадач", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("ИзмененоБизнесПроцессов", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("ИзмененоЗадач", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("ИзмененоСправочников",	Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("Ошибок", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("Предупреждений", Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ИсходныеДанные.Колонки.Добавить("ВидОбъекта", Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
	ИсходныеДанные.Колонки.Добавить("ОбъектСправочникДокумент");
	
	// Расчет максимального количества одновременных сессий.
	ОдновременноСессий = Новый ТаблицаЗначений();
	ОдновременноСессий.Колонки.Добавить("ДатаОдновременноРаботавшихПользователей",
		Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.Дата)));
	ОдновременноСессий.Колонки.Добавить("ОдновременноРаботавшихПользователей",
		Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ОдновременноСессий.Колонки.Добавить("СписокОдновременноРаботавшихПользователей");
	
	ДанныеЖурналаРегистрации = Новый ТаблицаЗначений;
	
	Уровни = Новый Массив;
	Уровни.Добавить(УровеньЖурналаРегистрации.Информация);
	
	События = Новый Массив;
	События.Добавить("_$Session$_.Start"); //  Начало сессии
	События.Добавить("_$Session$_.Finish"); //  Конец сессии  
	События.Добавить("_$Data$_.New"); // Добавление данных
	События.Добавить("_$Data$_.Update"); // Изменение данных
	
	ИмяПриложения = Новый Массив;
	ИмяПриложения.Добавить("1CV8C");
	ИмяПриложения.Добавить("WebClient");
	ИмяПриложения.Добавить("1CV8");
	
	ПользовательФильтр = Новый Массив;
	
	// Получаем список пользователей.
	Если ВариантОтчета = "АктивностьПользователя" Тогда
		ПользовательФильтр.Добавить(ИмяПользователяИБ(Пользователь));
	ИначеЕсли ТипЗнч(ПользователиИГруппы) = Тип("СписокЗначений") Тогда
		ПользователиДляАнализа(ПользовательФильтр, ПользователиИГруппы.ВыгрузитьЗначения());
	Иначе
		ПользователиДляАнализа(ПользовательФильтр, ПользователиИГруппы);
	КонецЕсли;
	
	ДатыВЧасовомПоясеСервера = ОбщегоНазначенияКлиентСервер.СвойствоСтруктуры(ПараметрыОтчета, "ДатыВЧасовомПоясеСервера", Ложь);
	Если ДатыВЧасовомПоясеСервера Тогда
		СмещениеВремениСервера = 0;
	Иначе
		СмещениеВремениСервера = ЖурналРегистрации.СмещениеВремениСервера();
	КонецЕсли;
	
	ОтборЖурналаРегистрации = Новый Структура;
	ОтборЖурналаРегистрации.Вставить("ДатаНачала", ДатаНачала + СмещениеВремениСервера);
	ОтборЖурналаРегистрации.Вставить("ДатаОкончания", ДатаОкончания + СмещениеВремениСервера);
	ОтборЖурналаРегистрации.Вставить("ИмяПриложения", ИмяПриложения);
	ОтборЖурналаРегистрации.Вставить("Уровень", Уровни);
	ОтборЖурналаРегистрации.Вставить("Событие", События);
	
	Если ПользовательФильтр.Количество() = 0 Тогда
		Возврат Новый Структура("АнализАктивностиПользователей, ОдновременноСессий, ОтчетПустой", ИсходныеДанные, ОдновременноСессий, Истина);
	КонецЕсли;
	
	Если ПользовательФильтр.Найти("ВсеПользователи") = Неопределено Тогда
		ОтборЖурналаРегистрации.Вставить("Пользователь", ПользовательФильтр);
	КонецЕсли;
	
	УстановитьПривилегированныйРежим(Истина);
	ВыгрузитьЖурналРегистрации(ДанныеЖурналаРегистрации, ОтборЖурналаРегистрации);
	УстановитьПривилегированныйРежим(Ложь);
	
	ОтчетПустой = (ДанныеЖурналаРегистрации.Количество() = 0);
	
	ДанныеЖурналаРегистрации.Сортировать("Сеанс, Дата");
	
	// Добавляем соответствие УникальныйИдентификатор-ПользовательСсылка для дальнейшего использования.
	ИдентификаторыПользователей = ДанныеЖурналаРегистрации.ВыгрузитьКолонку("Пользователь");
	СоответствиеИдентификаторыПользователей = УникальныеИдентификаторыПользователей(ИдентификаторыПользователей);
	
	ТекущийСеанс        = Неопределено;
	ЧасовРаботы         = 0;
	КоличествоЗапусков  = 0;
	СозданоДокументов   = 0;
	СозданоСправочников = 0;
	ИзмененоДокументов  = 0;
	ИзмененоСправочников= 0;
	ВидОбъекта          = Неопределено;
	СтрокаИсходныхДанных= Неопределено;
	НачалоСеанса        = Неопределено;
	
	// Расчет данных, необходимых для составления отчета.
	Для Каждого СтрокаДанныхЖурналаРегистрации Из ДанныеЖурналаРегистрации Цикл
		СозданоДокументов       = 0;
		СозданоСправочников     = 0;
		ИзмененоДокументов      = 0;
		ИзмененоСправочников    = 0;
		СозданоБизнесПроцессов  = 0;
		ИзмененоБизнесПроцессов = 0;
		ИзмененоЗадач           = 0;
		СозданоЗадач            = 0;
		ВидОбъекта              = Неопределено;
		
		СтрокаДанныхЖурналаРегистрации.Дата = СтрокаДанныхЖурналаРегистрации.Дата - СмещениеВремениСервера;
		Если СтрокаДанныхЖурналаРегистрации.ИмяПользователя = "" Тогда
			Продолжить;
		КонецЕсли;
		Сеанс = СтрокаДанныхЖурналаРегистрации.Сеанс; 
		
		Если Не ЗначениеЗаполнено(СтрокаДанныхЖурналаРегистрации.Сеанс)
			Или Не ЗначениеЗаполнено(СтрокаДанныхЖурналаРегистрации.Дата) Тогда
			Продолжить;
		КонецЕсли;
		
		ИмяПользователяСсылка = СоответствиеИдентификаторыПользователей[СтрокаДанныхЖурналаРегистрации.Пользователь];
		
		// Расчет продолжительности работы пользователей и количества запусков программы.
		Если ТекущийСеанс <> Сеанс
			Или СтрокаДанныхЖурналаРегистрации.Событие = "_$Session$_.Start" Тогда
			Если СтрокаИсходныхДанных <> Неопределено Тогда
				СтрокаИсходныхДанных.ЧасовРаботы  = ЧасовРаботы;
				СтрокаИсходныхДанных.КоличествоЗапусков = КоличествоЗапусков;
			КонецЕсли;
			СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
			СтрокаИсходныхДанных.Дата		  = СтрокаДанныхЖурналаРегистрации.Дата;
			СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата);
			СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
			ЧасовРаботы			= 0;
			КоличествоЗапусков	= 0; 
			ТекущийСеанс			= Сеанс; 
			НачалоСеанса		= СтрокаДанныхЖурналаРегистрации.Дата;
		КонецЕсли;
		
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Session$_.Finish" Тогда
			
			КоличествоЗапусков	= КоличествоЗапусков + 1;
			Если НачалоСеанса <> Неопределено Тогда 
				
				// Проверяем закончился сеанс пользователя в этот же день или на следующий.
				Если НачалоДня(СтрокаДанныхЖурналаРегистрации.Дата) > НачалоДня(НачалоСеанса) Тогда
					// Если окончание сеанса произошло на следующий день, то необходимо заполнить часы работы за предыдущий день.
					Разность = КонецДня(НачалоСеанса) - НачалоСеанса;
					ЧасовРаботы = Разность/60/60;
					СтрокаИсходныхДанных.ЧасовРаботы = ЧасовРаботы;
					ДеньСеанса = КонецДня(НачалоСеанса) + 86400;
					Пока КонецДня(СтрокаДанныхЖурналаРегистрации.Дата) > ДеньСеанса Цикл
						СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
						СтрокаИсходныхДанных.Дата = ДеньСеанса;
						СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата);
						СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
						ЧасовРаботы = (ДеньСеанса - НачалоДня(ДеньСеанса))/60/60;
						СтрокаИсходныхДанных.ЧасовРаботы  = ЧасовРаботы;
						ДеньСеанса = ДеньСеанса + 86400;
					КонецЦикла;	
					СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
					СтрокаИсходныхДанных.Дата = СтрокаДанныхЖурналаРегистрации.Дата;
					СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата);
					СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
					ЧасовРаботы = (СтрокаДанныхЖурналаРегистрации.Дата - НачалоДня(ДеньСеанса))/60/60;
					СтрокаИсходныхДанных.ЧасовРаботы  = ЧасовРаботы;
				Иначе
					Разность =  (СтрокаДанныхЖурналаРегистрации.Дата - НачалоСеанса)/60/60;
					ЧасовРаботы = ЧасовРаботы + Разность;
				КонецЕсли;
				
			КонецЕсли;
			
		КонецЕсли;
		
		// Расчет количества созданных документов и справочников.
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Data$_.New" Тогда
			
			Если СтрНайти(СтрокаДанныхЖурналаРегистрации.Метаданные, "Документ.") > 0 
				И ВыводитьДокументы Тогда
				ВидОбъекта = СтрокаДанныхЖурналаРегистрации.ПредставлениеМетаданных;
				ОбъектСправочникДокумент = СтрокаДанныхЖурналаРегистрации.Данные;
				СозданоДокументов = СозданоДокументов + 1;
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Дата		  = СтрокаДанныхЖурналаРегистрации.Дата;
				СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
				СтрокаИсходныхДанных.ВидОбъекта = ВидОбъекта;
				СтрокаИсходныхДанных.СозданоДокументов = СозданоДокументов;
				СтрокаИсходныхДанных.ОбъектСправочникДокумент = ОбъектСправочникДокумент;
				СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата); 
			КонецЕсли;
			
			Если СтрНайти(СтрокаДанныхЖурналаРегистрации.Метаданные, "Справочник.") > 0
				И ВыводитьСправочники Тогда
				ВидОбъекта = СтрокаДанныхЖурналаРегистрации.ПредставлениеМетаданных;
				ОбъектСправочникДокумент = СтрокаДанныхЖурналаРегистрации.Данные;
				СозданоСправочников = СозданоСправочников + 1;
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Дата		  = СтрокаДанныхЖурналаРегистрации.Дата;
				СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
				СтрокаИсходныхДанных.ВидОбъекта = ВидОбъекта;
				СтрокаИсходныхДанных.СозданоСправочников = СозданоСправочников;
				СтрокаИсходныхДанных.ОбъектСправочникДокумент = ОбъектСправочникДокумент;
				СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата);
			КонецЕсли;
			
		КонецЕсли;
		
		// Расчет количества измененных документов и справочников.
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Data$_.Update" Тогда
			
			Если СтрНайти(СтрокаДанныхЖурналаРегистрации.Метаданные, "Документ.") > 0
				И ВыводитьДокументы Тогда
				ВидОбъекта = СтрокаДанныхЖурналаРегистрации.ПредставлениеМетаданных;
				ОбъектСправочникДокумент = СтрокаДанныхЖурналаРегистрации.Данные;
				ИзмененоДокументов = ИзмененоДокументов + 1;
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Дата		  = СтрокаДанныхЖурналаРегистрации.Дата;
				СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
				СтрокаИсходныхДанных.ИзмененоДокументов = ИзмененоДокументов;  	
				СтрокаИсходныхДанных.ВидОбъекта = ВидОбъекта;
				СтрокаИсходныхДанных.ОбъектСправочникДокумент = ОбъектСправочникДокумент;
				СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата);
			КонецЕсли;
			
			Если СтрНайти(СтрокаДанныхЖурналаРегистрации.Метаданные, "Справочник.") > 0
				И ВыводитьСправочники Тогда
				ВидОбъекта = СтрокаДанныхЖурналаРегистрации.ПредставлениеМетаданных;
				ОбъектСправочникДокумент = СтрокаДанныхЖурналаРегистрации.Данные;
				ИзмененоСправочников = ИзмененоСправочников + 1;
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Дата		  = СтрокаДанныхЖурналаРегистрации.Дата;
				СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
				СтрокаИсходныхДанных.ИзмененоСправочников = ИзмененоСправочников;
				СтрокаИсходныхДанных.ВидОбъекта = ВидОбъекта;
				СтрокаИсходныхДанных.ОбъектСправочникДокумент = ОбъектСправочникДокумент;
				СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата);
			КонецЕсли;
			
		КонецЕсли;
		
		// Расчет количества созданных БизнесПроцессов и Задач.
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Data$_.New" Тогда
			
			Если СтрНайти(СтрокаДанныхЖурналаРегистрации.Метаданные, "БизнесПроцесс.") > 0 
				И ВыводитьБизнесПроцессы Тогда
				ВидОбъекта = СтрокаДанныхЖурналаРегистрации.ПредставлениеМетаданных;
				ОбъектСправочникДокумент = СтрокаДанныхЖурналаРегистрации.Данные;
				СозданоБизнесПроцессов = СозданоБизнесПроцессов + 1;
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Дата		  = СтрокаДанныхЖурналаРегистрации.Дата;
				СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
				СтрокаИсходныхДанных.ВидОбъекта = ВидОбъекта;
				СтрокаИсходныхДанных.СозданоБизнесПроцессов = СозданоБизнесПроцессов;
				СтрокаИсходныхДанных.ОбъектСправочникДокумент = ОбъектСправочникДокумент;
				СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата); 
			КонецЕсли;
			
			Если СтрНайти(СтрокаДанныхЖурналаРегистрации.Метаданные, "Задача.") > 0 
				И ВыводитьЗадачи Тогда
				ВидОбъекта = СтрокаДанныхЖурналаРегистрации.ПредставлениеМетаданных;
				ОбъектСправочникДокумент = СтрокаДанныхЖурналаРегистрации.Данные;
				СозданоЗадач = СозданоЗадач + 1;
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Дата		  = СтрокаДанныхЖурналаРегистрации.Дата;
				СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
				СтрокаИсходныхДанных.ВидОбъекта = ВидОбъекта;
				СтрокаИсходныхДанных.СозданоЗадач = СозданоЗадач;
				СтрокаИсходныхДанных.ОбъектСправочникДокумент = ОбъектСправочникДокумент;
				СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата);
			КонецЕсли;
			
		КонецЕсли;
		
		// Расчет количества измененных БизнесПроцессов и задач.
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Data$_.Update" Тогда
			
			Если СтрНайти(СтрокаДанныхЖурналаРегистрации.Метаданные, "БизнесПроцесс.") > 0
				И ВыводитьБизнесПроцессы Тогда
				ВидОбъекта = СтрокаДанныхЖурналаРегистрации.ПредставлениеМетаданных;
				ОбъектСправочникДокумент = СтрокаДанныхЖурналаРегистрации.Данные;
				ИзмененоБизнесПроцессов = ИзмененоБизнесПроцессов + 1;
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Дата		  = СтрокаДанныхЖурналаРегистрации.Дата;
				СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
				СтрокаИсходныхДанных.ВидОбъекта = ВидОбъекта;
				СтрокаИсходныхДанных.ИзмененоБизнесПроцессов = ИзмененоБизнесПроцессов;
				СтрокаИсходныхДанных.ОбъектСправочникДокумент = ОбъектСправочникДокумент;
				СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата);
			КонецЕсли;
			
			Если СтрНайти(СтрокаДанныхЖурналаРегистрации.Метаданные, "Задача.") > 0 
				И ВыводитьЗадачи Тогда
				ВидОбъекта = СтрокаДанныхЖурналаРегистрации.ПредставлениеМетаданных;
				ОбъектСправочникДокумент = СтрокаДанныхЖурналаРегистрации.Данные;
				ИзмененоЗадач = ИзмененоЗадач + 1;
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Дата		  = СтрокаДанныхЖурналаРегистрации.Дата;
				СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
				СтрокаИсходныхДанных.ВидОбъекта = ВидОбъекта;
				СтрокаИсходныхДанных.ИзмененоЗадач = ИзмененоЗадач;
				СтрокаИсходныхДанных.ОбъектСправочникДокумент = ОбъектСправочникДокумент;
				СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата);
			КонецЕсли;
			
		КонецЕсли;
		
	КонецЦикла;
	
	Если СтрокаИсходныхДанных <> Неопределено Тогда
		СтрокаИсходныхДанных.ЧасовРаботы  = ЧасовРаботы;
		СтрокаИсходныхДанных.КоличествоЗапусков = КоличествоЗапусков;
	КонецЕсли;
	
	Если ВариантОтчета = "АнализАктивностиПользователей" Тогда
	
		ДанныеЖурналаРегистрации.Сортировать("Дата");
		
		МассивПользователей 	= Новый Массив;
		МаксМассивПользователей = Новый Массив;
		ОдновременноРаботавшихПользователей  = 0;
		Счетчик                 = 0;
		ТекущаяДата             = Неопределено;
		
		Для Каждого СтрокаДанныхЖурналаРегистрации Из ДанныеЖурналаРегистрации Цикл
			
			Если Не ЗначениеЗаполнено(СтрокаДанныхЖурналаРегистрации.Дата)
				Или СтрокаДанныхЖурналаРегистрации.ИмяПользователя = "" Тогда
				Продолжить;
			КонецЕсли;
			
			ИмяПользователяСсылка = СоответствиеИдентификаторыПользователей[СтрокаДанныхЖурналаРегистрации.Пользователь];
			Если ИмяПользователяСсылка = Неопределено Тогда
				Продолжить;
			КонецЕсли;
			
			ИмяПользователяСтрока = ИмяПользователяИБ(ИмяПользователяСсылка);
			
			ДатаОдновременноРаботавшихПользователей = НачалоДня(СтрокаДанныхЖурналаРегистрации.Дата);
			
			// При смене дня обнуляем все данные по одновременным сессиям и заполняем данные за прошедший день.
			Если ТекущаяДата <> ДатаОдновременноРаботавшихПользователей Тогда
				Если ОдновременноРаботавшихПользователей <> 0 Тогда
					СформироватьСтрокуОдновременноСессий(ОдновременноСессий, МаксМассивПользователей, 
						ОдновременноРаботавшихПользователей, ТекущаяДата);
				КонецЕсли;
				ОдновременноРаботавшихПользователей = 0;
				Счетчик    = 0;
				МассивПользователей.Очистить();
				ТекущаяДата = ДатаОдновременноРаботавшихПользователей;
			КонецЕсли;
			
			Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Session$_.Start" Тогда
				Счетчик = Счетчик + 1;
				МассивПользователей.Добавить(ИмяПользователяСтрока);
			ИначеЕсли СтрокаДанныхЖурналаРегистрации.Событие = "_$Session$_.Finish" Тогда
				ИндексПользователя = МассивПользователей.Найти(ИмяПользователяСтрока);
				Если Не ИндексПользователя = Неопределено Тогда 
					МассивПользователей.Удалить(ИндексПользователя);
					Счетчик = Счетчик - 1;
				КонецЕсли;
			КонецЕсли;
			
			// Проверяем значение счетчика и сравниваем с максимальным значением.
			Счетчик = Макс(Счетчик, 0);
			Если Счетчик > ОдновременноРаботавшихПользователей Тогда
				МаксМассивПользователей = Новый Массив;
				Для Каждого Элемент Из МассивПользователей Цикл
					МаксМассивПользователей.Добавить(Элемент);
				КонецЦикла;
			КонецЕсли;
			ОдновременноРаботавшихПользователей = Макс(ОдновременноРаботавшихПользователей, Счетчик);
			
		КонецЦикла;
		
		Если ОдновременноРаботавшихПользователей <> 0 Тогда
			СформироватьСтрокуОдновременноСессий(ОдновременноСессий, МаксМассивПользователей, 
				ОдновременноРаботавшихПользователей, ТекущаяДата);
		КонецЕсли;
		
		// Расчет количества ошибок и предупреждений.
		ДанныеЖурналаРегистрации = Неопределено;
		Ошибок 					 = 0;
		Предупреждений			 = 0;
		ДанныеЖурналаРегистрации = ИнформацияПоОшибкамЖурналаРегистрации(ДатаНачала, ДатаОкончания, СмещениеВремениСервера);
		
		ОтчетПустой =  ОтчетПустой Или (ДанныеЖурналаРегистрации.Количество() = 0);
		
		Для Каждого СтрокаДанныхЖурналаРегистрации Из ДанныеЖурналаРегистрации Цикл
			
			Если СтрокаДанныхЖурналаРегистрации.ИмяПользователя = "" Тогда
				Продолжить;
			КонецЕсли;
			
			Если ПользовательФильтр.Найти(СтрокаДанныхЖурналаРегистрации.ИмяПользователя) = Неопределено
				И ПользовательФильтр.Количество() <> 0 Тогда
				Продолжить;
			КонецЕсли;
			
			ИмяПользователяСсылка = СоответствиеИдентификаторыПользователей[СтрокаДанныхЖурналаРегистрации.Пользователь];
			
			Если СтрокаДанныхЖурналаРегистрации.Уровень = УровеньЖурналаРегистрации.Ошибка Тогда
				Ошибок = Ошибок + 1;
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Дата = СтрокаДанныхЖурналаРегистрации.Дата;
				СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата);
				СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
				СтрокаИсходныхДанных.Ошибок = Ошибок;
			КонецЕсли;
			
			Если СтрокаДанныхЖурналаРегистрации.Уровень = УровеньЖурналаРегистрации.Предупреждение Тогда
				Предупреждений = Предупреждений + 1;
				СтрокаИсходныхДанных = ИсходныеДанные.Добавить();
				СтрокаИсходныхДанных.Дата = СтрокаДанныхЖурналаРегистрации.Дата;
				СтрокаИсходныхДанных.Неделя 	  = СтрокаНеделяГода(СтрокаДанныхЖурналаРегистрации.Дата);
				СтрокаИсходныхДанных.Пользователь = ИмяПользователяСсылка;
				СтрокаИсходныхДанных.Предупреждений = Предупреждений;
			КонецЕсли;
			
			Ошибок         = 0;
			Предупреждений = 0;
		КонецЦикла;
		
	КонецЕсли;
	
	Возврат Новый Структура("АнализАктивностиПользователей, ОдновременноСессий, ОтчетПустой", ИсходныеДанные, ОдновременноСессий, ОтчетПустой);
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Анализ активности пользователей.

Функция ПользователиДляАнализа(ПользовательФильтр, Знач ПользователиИГруппы)
	
	Если Не ТипЗнч(ПользователиИГруппы) = Тип("Массив") Тогда
		ПользователиИГруппы = ОбщегоНазначенияКлиентСервер.ЗначениеВМассиве(ПользователиИГруппы);
	КонецЕсли;
	
	ГруппыДляПолученияПользователей = Новый Массив;
	ВсеПользователи = Справочники.ГруппыПользователей.ВсеПользователи;
	Для Каждого ПользовательИлиГруппа Из ПользователиИГруппы Цикл
		Если ТипЗнч(ПользовательИлиГруппа) = Тип("СправочникСсылка.Пользователи") Тогда
			ИмяПользователяИБ = ИмяПользователяИБ(ПользовательИлиГруппа);
			
			Если ИмяПользователяИБ <> Неопределено Тогда
				ПользовательФильтр.Добавить(ИмяПользователяИБ);
			КонецЕсли;
		ИначеЕсли ПользовательИлиГруппа = ВсеПользователи Тогда
			ПользовательФильтр.Добавить("ВсеПользователи");
			Возврат ПользовательФильтр;
		ИначеЕсли ТипЗнч(ПользовательИлиГруппа) = Тип("СправочникСсылка.ГруппыПользователей") Тогда
			ГруппыДляПолученияПользователей.Добавить(ПользовательИлиГруппа);
		КонецЕсли;
	КонецЦикла;
	
	Если ГруппыДляПолученияПользователей.Количество() > 0 Тогда
		
		Запрос = Новый Запрос;
		Запрос.УстановитьПараметр("Группа", ГруппыДляПолученияПользователей);
		Запрос.Текст = 
			"ВЫБРАТЬ РАЗЛИЧНЫЕ
			|	СоставыГруппПользователей.Пользователь КАК Пользователь
			|ИЗ
			|	РегистрСведений.СоставыГруппПользователей КАК СоставыГруппПользователей
			|ГДЕ
			|	СоставыГруппПользователей.ГруппаПользователей В
			|			(ВЫБРАТЬ
			|				ГруппыПользователей.Ссылка КАК Ссылка
			|			ИЗ
			|				Справочник.ГруппыПользователей КАК ГруппыПользователей
			|			ГДЕ
			|				ГруппыПользователей.Ссылка В ИЕРАРХИИ (&Группа))";
		Результат = Запрос.Выполнить().Выгрузить();
		
		Для Каждого Строка Из Результат Цикл
			ИмяПользователяИБ = ИмяПользователяИБ(Строка.Пользователь);
			
			Если ИмяПользователяИБ <> Неопределено Тогда
				ПользовательФильтр.Добавить(ИмяПользователяИБ);
			КонецЕсли;
		
		КонецЦикла;
		
	КонецЕсли;
	
	Возврат ПользовательФильтр;
КонецФункции

Функция УникальныеИдентификаторыПользователей(ИдентификаторыПользователей)
	МассивУникальныеИдентификаторыПользователей = Новый Массив;
	
	ОбщегоНазначенияКлиентСервер.ДополнитьМассив(МассивУникальныеИдентификаторыПользователей,
		ИдентификаторыПользователей, Истина);
	СоответствиеУИД = Новый Соответствие;
	Для Каждого Элемент Из МассивУникальныеИдентификаторыПользователей Цикл
		
		Если ЗначениеЗаполнено(Элемент) Тогда
			ИмяПользователяСсылка = ПользовательСсылка(Элемент);
			ИдентификаторПользователяИБ = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ИмяПользователяСсылка, "ИдентификаторПользователяИБ");
			
			Если ИдентификаторПользователяИБ <> Неопределено Тогда
				СоответствиеУИД.Вставить(Элемент, ИмяПользователяСсылка);
			КонецЕсли;
			
		КонецЕсли;
		
	КонецЦикла;
	
	Возврат СоответствиеУИД;
КонецФункции

Функция ПользовательСсылка(ПользовательУИД)
	Возврат Справочники.Пользователи.НайтиПоРеквизиту("ИдентификаторПользователяИБ", ПользовательУИД);
КонецФункции

Функция ИмяПользователяИБ(ПользовательСсылка) Экспорт
	УстановитьПривилегированныйРежим(Истина);
	ИдентификаторПользователяИБ = ОбщегоНазначения.ЗначениеРеквизитаОбъекта(ПользовательСсылка, "ИдентификаторПользователяИБ");
	ПользовательИБ = ПользователиИнформационнойБазы.НайтиПоУникальномуИдентификатору(ИдентификаторПользователяИБ);
	
	Если ПользовательИБ <> Неопределено Тогда
		Возврат ПользовательИБ.Имя; 
	Иначе
		Возврат Неопределено;
	КонецЕсли;
	
КонецФункции

Функция СтрокаНеделяГода(ДатаГода)
	Возврат СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Неделя %1'"), НеделяГода(ДатаГода));
КонецФункции

Процедура СформироватьСтрокуОдновременноСессий(ОдновременноСессий, МаксМассивПользователей,
			ОдновременноРаботавшихПользователей, ТекущаяДата)
	
	ВременныйМассив = Новый Массив;
	Индекс = 0;
	Для Каждого Элемент Из МаксМассивПользователей Цикл
		ВременныйМассив.Вставить(Индекс, Элемент);
		СчетчикКоличестваСеансовПользователя = 0;
		
		Для Каждого ИмяПользователя Из ВременныйМассив Цикл
			Если ИмяПользователя = Элемент Тогда
				СчетчикКоличестваСеансовПользователя = СчетчикКоличестваСеансовПользователя + 1;
				ПользовательИНомер = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 (%2)'"),
					Элемент,
					СчетчикКоличестваСеансовПользователя);
			КонецЕсли;
		КонецЦикла;
		
		СтрокаТаблицы = ОдновременноСессий.Добавить();
		СтрокаТаблицы.ДатаОдновременноРаботавшихПользователей = ТекущаяДата;
		СтрокаТаблицы.ОдновременноРаботавшихПользователей = ОдновременноРаботавшихПользователей;
		СтрокаТаблицы.СписокОдновременноРаботавшихПользователей = ПользовательИНомер;
		Индекс = Индекс + 1;
	КонецЦикла;
	
КонецПроцедуры

////////////////////////////////////////////////////////////////////////////////
// Продолжительность работы регламентных заданий.

// Функция формирует отчет по работе регламентных заданий.
//
// Параметры:
//   ПараметрыЗаполнения - Структура - набор параметров, необходимых для построения отчета, где:
//     * ДатаНачала    - Дата - начало периода, по которому будет собираться информация.
//     * ДатаОкончания - Дата - окончание периода, по которому будет собираться информация.
//   РазмерОдновременноСессий - Число - минимальное количество одновременно работавших регламентных заданий для
//                                      отображения в таблице.
//   МинимальнаяПродолжительностьСеансовРегламентныхЗаданий - Число - минимальная продолжительность сеансов регламентных
//                                                                    заданий в секундах.
//   ОтображатьФоновыеЗадания - Булево - если истина, на диаграмме ганта будет выводиться строка с интервалами сеансов
//                                       фоновых заданий.
//   ВыводитьЗаголовок - ТипВыводаТекстаКомпоновкиДанных - предназначен для отключения/включения заголовка.
//   ВыводитьОтбор - ТипВыводаТекстаКомпоновкиДанных - предназначен для отключения/включения отображения отбора.
//   СкрытьРегламентныеЗадания - СписокЗначений - список регламентных заданий, которые необходимо исключить из отчета.
//
Функция СформироватьОтчетПоПродолжительностиРаботыРегламентныхЗаданий(ПараметрыЗаполнения) Экспорт
	
	// Параметры отчета
	ДатаНачала = ПараметрыЗаполнения.ДатаНачала;
	ДатаОкончания = ПараметрыЗаполнения.ДатаОкончания;
	МинимальнаяПродолжительностьСеансовРегламентныхЗаданий = 
		ПараметрыЗаполнения.МинимальнаяПродолжительностьСеансовРегламентныхЗаданий;
	ВыводитьЗаголовок = ПараметрыЗаполнения.ВыводитьЗаголовок;
	ВыводитьОтбор = ПараметрыЗаполнения.ВыводитьОтбор;
	
	Результат = Новый Структура;
	Отчет = Новый ТабличныйДокумент;
	
	// Получаем данные для построения отчета.
	ПолучитьДанные = ДанныеДляОтчетаПоПродолжительностиРаботыРегламентныхЗаданий(ПараметрыЗаполнения);
	ТаблицаСеансыРегламентныхЗаданий = ПолучитьДанные.ТаблицаСеансыРегламентныхЗаданий;
	ОдновременноСессий = ПолучитьДанные.ИтогоОдновременноРегламентныхЗаданий;
	КоличествоЗапусков = ПолучитьДанные.КоличествоЗапусков;
	ОтчетПустой        = ПолучитьДанные.ОтчетПустой;
	Макет = ПолучитьМакет("ПродолжительностьРаботыРегламентныхЗаданий");
	
	// Набор цветов для фона диаграммы и таблицы.
	ЦветаФона = Новый Массив;
	ЦветаФона.Добавить(WebЦвета.Белый);
	ЦветаФона.Добавить(WebЦвета.СветлоЖелтый);
	ЦветаФона.Добавить(WebЦвета.Лимонный);
	ЦветаФона.Добавить(WebЦвета.НавахоБелый);
	
	// Формируем шапку отчета
	Если ВыводитьЗаголовок.Значение = ТипВыводаТекстаКомпоновкиДанных.Выводить
		И ВыводитьЗаголовок.Использование
		ИЛИ Не ВыводитьЗаголовок.Использование Тогда
		Отчет.Вывести(ОписаниеОбластиМакета(Макет, "ШапкаОтчета"));
	КонецЕсли;
	
	Если ВыводитьОтбор.Значение = ТипВыводаТекстаКомпоновкиДанных.Выводить
		И ВыводитьОтбор.Использование
		Или Не ВыводитьОтбор.Использование Тогда
		Область = ОписаниеОбластиМакета(Макет, "Отбор");
		Если МинимальнаяПродолжительностьСеансовРегламентныхЗаданий > 0 Тогда
			РежимОтображенияИнтервалов = НСтр("ru = 'Отключено отображение интервалов с нулевой продолжительностью'");
		Иначе
			РежимОтображенияИнтервалов = НСтр("ru = 'Включено отображение интервалов с нулевой продолжительностью'");
		КонецЕсли;
		Область.Параметры.ДатаНачала = ДатаНачала;
		Область.Параметры.ДатаОкончания = ДатаОкончания;
		Область.Параметры.РежимОтображенияИнтервалов = РежимОтображенияИнтервалов;
		Отчет.Вывести(Область);
	КонецЕсли;
	
	Если ЗначениеЗаполнено(ОдновременноСессий) Тогда
	
		Отчет.Вывести(ОписаниеОбластиМакета(Макет, "ШапкаТаблицы"));
		
		// Формируем таблицу максимального количества одновременно запущенных РЗ.
		ТекущееКоличествоСессий = 0; 
		ИндексЦвета = 3;
		Для Каждого СтрокаОдновременноСессий Из ОдновременноСессий Цикл
			Область = ОписаниеОбластиМакета(Макет, "Таблица");
			Если ТекущееКоличествоСессий <> 0 
				И ТекущееКоличествоСессий <> СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий
				И ИндексЦвета <> 0 Тогда
				ИндексЦвета = ИндексЦвета - 1;
			КонецЕсли;
			Если СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий = 1 Тогда
				ИндексЦвета = 0;
			КонецЕсли;
			Область.Параметры.Заполнить(СтрокаОдновременноСессий);
			ЦветФонаТаблицы = ЦветаФона.Получить(ИндексЦвета);
			ОбластьТаблицы = Область.Области.Таблица; // ОбластьЯчеекТабличногоДокумента
			ОбластьТаблицы.ЦветФона = ЦветФонаТаблицы;
			Отчет.Вывести(Область);
			ТекущееКоличествоСессий = СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий;
			МассивРегламентныхЗаданий = СтрокаОдновременноСессий.СписокРегламентныхЗаданий;
			ИндексРегламентногоЗадания = 0;
			Отчет.НачатьГруппуСтрок(, Ложь);
			Для Каждого Элемент Из МассивРегламентныхЗаданий Цикл
				Если Не ТипЗнч(Элемент) = Тип("Число")
					И Не ТипЗнч(Элемент) = Тип("Дата") Тогда
					Область = ОписаниеОбластиМакета(Макет, "СписокРегламентныхЗаданий");
					Область.Параметры.СписокРегламентныхЗаданий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 (сеанс %2)'"),
						Элемент,
						МассивРегламентныхЗаданий[ИндексРегламентногоЗадания+1]);
				ИначеЕсли Не ТипЗнч(Элемент) = Тип("Дата")
					И Не ТипЗнч(Элемент) = Тип("Строка") Тогда	
					Область.Параметры.РасшифровкаЗадания = Новый Массив;
					Область.Параметры.РасшифровкаЗадания.Добавить("РасшифровкаРегламентногоЗадания");
					Область.Параметры.РасшифровкаЗадания.Добавить(Элемент);
					ИмяРегламентногоЗадания = МассивРегламентныхЗаданий.Получить(ИндексРегламентногоЗадания-1);
					Область.Параметры.РасшифровкаЗадания.Добавить(ИмяРегламентногоЗадания);
					Область.Параметры.РасшифровкаЗадания.Добавить(ДатаНачала);
					Область.Параметры.РасшифровкаЗадания.Добавить(ДатаОкончания);
					Отчет.Вывести(Область);
				КонецЕсли;
				ИндексРегламентногоЗадания = ИндексРегламентногоЗадания + 1;
			КонецЦикла;
			Отчет.ЗакончитьГруппуСтрок();
		КонецЦикла;
	КонецЕсли;
	
	Отчет.Вывести(ОписаниеОбластиМакета(Макет, "ПустаяСтрока"));
	
	// Получаем ДиаграммуГанта и задаем параметры, необходимые для ее заполнения.
	Область = ОписаниеОбластиМакета(Макет, "Диаграмма");
	ДиаграммаГанта = Область.Рисунки.ДиаграммаГанта.Объект; // ДиаграммаГанта
	ДиаграммаГанта.Обновление = Ложь;  
	
	Серия = ДиаграммаГанта.Серии.Добавить();

	ТекущееСобытие			 = Неопределено;
	ОбщаяПродолжительностьРЗ = 0;
	Точка					 = Неопределено;
	СтрокаКоличествоЗапусков = Неопределено;
	ЗапусковРегламентногоЗадания = 0;
	ПризнакСменыТочки        = Ложь;
	
	// Заполняем диаграмму ганта	
	Для Каждого СтрокаРегламентныеЗадания Из ТаблицаСеансыРегламентныхЗаданий Цикл
		ДлительностьИнтервалаРегламентногоЗадания =
			СтрокаРегламентныеЗадания.ДатаОкончанияЗадания - СтрокаРегламентныеЗадания.ДатаЗапускаЗадания;
		Если ДлительностьИнтервалаРегламентногоЗадания >= МинимальнаяПродолжительностьСеансовРегламентныхЗаданий Тогда
			Если ТекущееСобытие <> СтрокаРегламентныеЗадания.НазваниеСобытия Тогда
				Если ТекущееСобытие <> Неопределено
					И ПризнакСменыТочки Тогда
					РасшифровкаТочки = Точка.Расшифровка; // Массив
					РасшифровкаТочки.Добавить(ЗапусковРегламентногоЗадания);
					РасшифровкаТочки.Добавить(ОбщаяПродолжительностьРЗ);
					РасшифровкаТочки.Добавить(ДатаНачала);
					РасшифровкаТочки.Добавить(ДатаОкончания);
					ТочкаНазвание = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 (%2 из %3)'"),
						Точка.Значение,
						ЗапусковРегламентногоЗадания,
						Строка(СтрокаКоличествоЗапусков.Запусков));
					Точка.Значение = ТочкаНазвание;
				КонецЕсли;
				СтрокаКоличествоЗапусков = КоличествоЗапусков.Найти(
					СтрокаРегламентныеЗадания.НазваниеСобытия, "НазваниеСобытия");
				// Не заполняем расшифровку для фоновых заданий.
				Если СтрокаРегламентныеЗадания.СобытиеМетаданные <> "" Тогда 
					ТочкаНазвание = СтрокаРегламентныеЗадания.НазваниеСобытия;
					Точка = ДиаграммаГанта.УстановитьТочку(ТочкаНазвание);
					РасшифровкаТочки  = Новый Массив;
					ИнтервалНачало	  = Новый Массив;
					ИнтервалКонец	  = Новый Массив;
					СеансРегламентногоЗадания = Новый Массив;
					РасшифровкаТочки.Добавить("РасшифровкаТочки");
					РасшифровкаТочки.Добавить(СтрокаРегламентныеЗадания.СобытиеМетаданные);
					РасшифровкаТочки.Добавить(СтрокаРегламентныеЗадания.НазваниеСобытия);
					РасшифровкаТочки.Добавить(СтрокаКоличествоЗапусков.Отменено);
					РасшифровкаТочки.Добавить(СтрокаКоличествоЗапусков.ОшибкаВыполнения);                                                             
					РасшифровкаТочки.Добавить(ИнтервалНачало);
					РасшифровкаТочки.Добавить(ИнтервалКонец);
					РасшифровкаТочки.Добавить(СеансРегламентногоЗадания);
					РасшифровкаТочки.Добавить(МинимальнаяПродолжительностьСеансовРегламентныхЗаданий);
					Точка.Расшифровка = РасшифровкаТочки;
					ТекущееСобытие = СтрокаРегламентныеЗадания.НазваниеСобытия;
					ОбщаяПродолжительностьРЗ = 0;				
					ЗапусковРегламентногоЗадания = 0;
					Точка.Картинка = БиблиотекаКартинок.РегламентноеЗадание;
				ИначеЕсли Не ЗначениеЗаполнено(СтрокаРегламентныеЗадания.СобытиеМетаданные) Тогда
					ТочкаНазвание = НСтр("ru = 'Фоновые задания'");
					Точка = ДиаграммаГанта.УстановитьТочку(ТочкаНазвание);
					ОбщаяПродолжительностьРЗ = 0;
				КонецЕсли;
			КонецЕсли;
			Значение = ДиаграммаГанта.ПолучитьЗначение(Точка, Серия);
			Интервал = Значение.Добавить();
			Интервал.Начало = СтрокаРегламентныеЗадания.ДатаЗапускаЗадания;
			Интервал.Конец = СтрокаРегламентныеЗадания.ДатаОкончанияЗадания;
			Интервал.Текст = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 - %2'"),
				Формат(Интервал.Начало, "ДЛФ=T"),
				Формат(Интервал.Конец, "ДЛФ=T"));
			ПризнакСменыТочки = Ложь;
			// Не заполняем расшифровку для фоновых заданий.
			Если СтрокаРегламентныеЗадания.СобытиеМетаданные <> "" Тогда
				ИнтервалНачало.Добавить(СтрокаРегламентныеЗадания.ДатаЗапускаЗадания);
				ИнтервалКонец.Добавить(СтрокаРегламентныеЗадания.ДатаОкончанияЗадания);
				СеансРегламентногоЗадания.Добавить(СтрокаРегламентныеЗадания.Сеанс);
				ОбщаяПродолжительностьРЗ = ДлительностьИнтервалаРегламентногоЗадания + ОбщаяПродолжительностьРЗ;
				ЗапусковРегламентногоЗадания = ЗапусковРегламентногоЗадания + 1;
				ПризнакСменыТочки = Истина;
			КонецЕсли;
		КонецЕсли;
	КонецЦикла; 
	
	Если ЗапусковРегламентногоЗадания <> 0
		И ЗначениеЗаполнено(Точка.Расшифровка) Тогда
		// Присваиваем расшифровку последней точке.
		РасшифровкаТочки = Точка.Расшифровка; // Массив
		РасшифровкаТочки.Добавить(ЗапусковРегламентногоЗадания);
		РасшифровкаТочки.Добавить(ОбщаяПродолжительностьРЗ);
		РасшифровкаТочки.Добавить(ДатаНачала);
		РасшифровкаТочки.Добавить(ДатаОкончания);	
		ТочкаНазвание = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 (%2 из %3)'"),
			Точка.Значение,
			ЗапусковРегламентногоЗадания,
			Строка(СтрокаКоличествоЗапусков.Запусков));
		Точка.Значение = ТочкаНазвание;
	КонецЕсли;
		
	// Устанавливаем настройки отображения диаграммы.
	ЦветаДиаграммыГанта(ДатаНачала, ДиаграммаГанта, ОдновременноСессий, ЦветаФона);
	ПериодАнализа = ДатаОкончания - ДатаНачала;
	ШкалаВремениДиаграммыГанта(ДиаграммаГанта, ПериодАнализа);
	
	КоличествоКолонок = ДиаграммаГанта.Точки.Количество();
	Область.Рисунки.ДиаграммаГанта.Высота				 = 15 + 10 * КоличествоКолонок;
	Область.Рисунки.ДиаграммаГанта.Ширина 				 = 450;
	ДиаграммаГанта.АвтоОпределениеПолногоИнтервала	 = Ложь; 
	ДиаграммаГанта.ОтображениеИнтервала   			 = ОтображениеИнтервалаДиаграммыГанта.Плоский;
	ДиаграммаГанта.ОбластьЛегенды.Расположение       = РасположениеЛегендыДиаграммы.Нет;
	ДиаграммаГанта.РастягиваниеПоВертикали 			 = РастягиваниеПоВертикалиДиаграммыГанта.РастягиватьСтрокиИДанные;
	ДиаграммаГанта.УстановитьПолныйИнтервал(ДатаНачала, ДатаОкончания);
	ДиаграммаГанта.Обновление = Истина;

	Отчет.Вывести(Область);
	
	Результат.Вставить("Отчет", Отчет);
	Результат.Вставить("ОтчетПустой", ОтчетПустой);
	Возврат Результат;
КонецФункции

// Функция получает информацию по регламентным заданиям из журнала регистрации.
//
// Параметры:
//   ПараметрыЗаполнения - Структура - набор параметров, необходимых для построения отчета:
//   * ДатаНачала    - Дата - начало периода, по которому будет собираться информация.
//   * ДатаОкончания - Дата - окончание периода, по которому будет собираться информация.
//   * РазмерОдновременноСессий	- Число - минимальное количество одновременно работавших регламентных
// 		заданий для отображения в таблице.
//   * МинимальнаяПродолжительностьСеансовРегламентныхЗаданий - Число - минимальная продолжительность
// 		сеансов регламентных заданий в секундах.
//   * ОтображатьФоновыеЗадания - Булево - если истина, на диаграмме ганта будет выводиться строка с 
// 		интервалами сеансов фоновых заданий.
//   * СкрытьРегламентныеЗадания - СписокЗначений - список регламентных заданий, которые необходимо исключить из отчета.
//
// Возвращаемое значение
//   ТаблицаЗначений - таблица, содержащая информацию по работе регламентных заданий
//     из журнала регистрации.
//
Функция ДанныеДляОтчетаПоПродолжительностиРаботыРегламентныхЗаданий(ПараметрыЗаполнения)
	
	ДатаНачала = ПараметрыЗаполнения.ДатаНачала;
	ДатаОкончания = ПараметрыЗаполнения.ДатаОкончания;
	РазмерОдновременноСессий = ПараметрыЗаполнения.РазмерОдновременноСессий;
	ОтображатьФоновыеЗадания = ПараметрыЗаполнения.ОтображатьФоновыеЗадания;
	МинимальнаяПродолжительностьСеансовРегламентныхЗаданий =
		ПараметрыЗаполнения.МинимальнаяПродолжительностьСеансовРегламентныхЗаданий;
	СкрытьРегламентныеЗадания = ПараметрыЗаполнения.СкрытьРегламентныеЗадания;
	СмещениеВремениСервера = ПараметрыЗаполнения.СмещениеВремениСервера;
	
	ДанныеЖурналаРегистрации = Новый ТаблицаЗначений;
	
	Уровни = Новый Массив;
	Уровни.Добавить(УровеньЖурналаРегистрации.Информация);
	Уровни.Добавить(УровеньЖурналаРегистрации.Предупреждение);
	Уровни.Добавить(УровеньЖурналаРегистрации.Ошибка);
	
	СобытияРЗ = Новый Массив;
	СобытияРЗ.Добавить("_$Job$_.Start");
	СобытияРЗ.Добавить("_$Job$_.Cancel");
	СобытияРЗ.Добавить("_$Job$_.Fail");
	СобытияРЗ.Добавить("_$Job$_.Succeed");
	СобытияРЗ.Добавить("_$Job$_.Finish");
	СобытияРЗ.Добавить("_$Job$_.Error");
	
	УстановитьПривилегированныйРежим(Истина);
	ОтборЖурнала = Новый Структура;
	ОтборЖурнала.Вставить("Уровень", Уровни);
	ОтборЖурнала.Вставить("ДатаНачала", ДатаНачала + СмещениеВремениСервера);
	ОтборЖурнала.Вставить("ДатаОкончания", ДатаОкончания + СмещениеВремениСервера);
	ОтборЖурнала.Вставить("Событие", СобытияРЗ);
	
	ВыгрузитьЖурналРегистрации(ДанныеЖурналаРегистрации, ОтборЖурнала);
	ОтчетПустой = (ДанныеЖурналаРегистрации.Количество() = 0);
	
	Если СмещениеВремениСервера <> 0 Тогда
		Для Каждого СтрокаТаблицы Из ДанныеЖурналаРегистрации Цикл
			СтрокаТаблицы.Дата = СтрокаТаблицы.Дата - СмещениеВремениСервера;
		КонецЦикла;
	КонецЕсли;
	
	// Формируем данные для отбора по регламентным заданиям.
	СписокВсехРегламентныхЗаданий = РегламентныеЗаданияСервер.НайтиЗадания(Новый Структура);
	СоответствиеМетаданныеИдентификатор = Новый Соответствие;
	СоответствиеМетаданныеНазвание = Новый Соответствие;
	СоответствиеНаименованиеИдентификатор = Новый Соответствие;
	УстановитьПривилегированныйРежим(Ложь);
	
	Для Каждого РегЗадание Из СписокВсехРегламентныхЗаданий Цикл
		СоответствиеМетаданныеИдентификатор.Вставить(РегЗадание.Метаданные, Строка(РегЗадание.УникальныйИдентификатор));
		СоответствиеНаименованиеИдентификатор.Вставить(РегЗадание.Наименование, Строка(РегЗадание.УникальныйИдентификатор));
		Если РегЗадание.Наименование <> "" Тогда
			СоответствиеМетаданныеНазвание.Вставить(РегЗадание.Метаданные, РегЗадание.Наименование);
		Иначе
			СоответствиеМетаданныеНазвание.Вставить(РегЗадание.Метаданные, РегЗадание.Метаданные.Синоним);
		КонецЕсли;
	КонецЦикла;
	
	// Заполняем параметры, необходимые для определения одновременно работавших регламентных заданий.
	ПараметрыОдновременноСессий = Новый Структура;
	ПараметрыОдновременноСессий.Вставить("ДанныеЖурналаРегистрации", ДанныеЖурналаРегистрации);
	ПараметрыОдновременноСессий.Вставить("СоответствиеНаименованиеИдентификатор", СоответствиеНаименованиеИдентификатор);
	ПараметрыОдновременноСессий.Вставить("СоответствиеМетаданныеИдентификатор", СоответствиеМетаданныеИдентификатор);
	ПараметрыОдновременноСессий.Вставить("СоответствиеМетаданныеНазвание", СоответствиеМетаданныеНазвание);
	ПараметрыОдновременноСессий.Вставить("СкрытьРегламентныеЗадания", СкрытьРегламентныеЗадания);
	ПараметрыОдновременноСессий.Вставить("МинимальнаяПродолжительностьСеансовРегламентныхЗаданий",
		МинимальнаяПродолжительностьСеансовРегламентныхЗаданий);
	
	// Максимальное количество одновременных сессий	регламентных заданий.
	ОдновременноСессий = ОдновременноРегламентныхЗаданий(ПараметрыОдновременноСессий);
	
	// Отбираем нужные значения из таблицы ОдновременноСессий.
	ОдновременноСессий.Сортировать("ОдновременноРегламентныхЗаданий Убыв");
	
	СтрокаИтогоОдновременноРегламентныхЗаданий = Неопределено;
	ИтогоОдновременноРегламентныхЗаданий = Новый ТаблицаЗначений();
	ИтогоОдновременноРегламентныхЗаданий.Колонки.Добавить("ДатаОдновременноРегламентныхЗаданий", 
		Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
	ИтогоОдновременноРегламентныхЗаданий.Колонки.Добавить("ОдновременноРегламентныхЗаданий", 
		Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10))); 
	ИтогоОдновременноРегламентныхЗаданий.Колонки.Добавить("СписокРегламентныхЗаданий");
	
	Для Каждого СтрокаОдновременноСессий Из ОдновременноСессий Цикл
		Если СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий >= РазмерОдновременноСессий
			И СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий >= 2 Тогда
			СтрокаИтогоОдновременноРегламентныхЗаданий = ИтогоОдновременноРегламентныхЗаданий.Добавить();
			СтрокаИтогоОдновременноРегламентныхЗаданий.ДатаОдновременноРегламентныхЗаданий = 
				СтрокаОдновременноСессий.ДатаОдновременноРегламентныхЗаданий;
			СтрокаИтогоОдновременноРегламентныхЗаданий.ОдновременноРегламентныхЗаданий = 
				СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий;
			СтрокаИтогоОдновременноРегламентныхЗаданий.СписокРегламентныхЗаданий = 
				СтрокаОдновременноСессий.СписокРегламентныхЗаданий;
		КонецЕсли;
	КонецЦикла;
	
	ДанныеЖурналаРегистрации.Сортировать("Метаданные, Данные, Дата, Сеанс");
	
	// Заполняем параметры, необходимые для получения данных по каждому сеансу регламентных заданий.
	ПараметрыСеансыРегламентныхЗаданий = Новый Структура;
	ПараметрыСеансыРегламентныхЗаданий.Вставить("ДанныеЖурналаРегистрации", ДанныеЖурналаРегистрации);
	ПараметрыСеансыРегламентныхЗаданий.Вставить("СоответствиеНаименованиеИдентификатор", СоответствиеНаименованиеИдентификатор);
	ПараметрыСеансыРегламентныхЗаданий.Вставить("СоответствиеМетаданныеИдентификатор", СоответствиеМетаданныеИдентификатор);
	ПараметрыСеансыРегламентныхЗаданий.Вставить("СоответствиеМетаданныеНазвание", СоответствиеМетаданныеНазвание);
	ПараметрыСеансыРегламентныхЗаданий.Вставить("ОтображатьФоновыеЗадания", ОтображатьФоновыеЗадания);
	ПараметрыСеансыРегламентныхЗаданий.Вставить("СкрытьРегламентныеЗадания", СкрытьРегламентныеЗадания);
	
	// Регламентные задания
	ТаблицаСеансыРегламентныхЗаданий = 
		СеансыРегламентныхЗаданий(ПараметрыСеансыРегламентныхЗаданий).ТаблицаСеансыРегламентныхЗаданий;
	КоличествоЗапусков = СеансыРегламентныхЗаданий(ПараметрыСеансыРегламентныхЗаданий).КоличествоЗапусков;
	
	Результат = Новый Структура;
	Результат.Вставить("ТаблицаСеансыРегламентныхЗаданий", ТаблицаСеансыРегламентныхЗаданий);
	Результат.Вставить("ИтогоОдновременноРегламентныхЗаданий", ИтогоОдновременноРегламентныхЗаданий);
	Результат.Вставить("КоличествоЗапусков", КоличествоЗапусков);
	Результат.Вставить("ОтчетПустой", ОтчетПустой);
	
	Возврат Результат;
КонецФункции

Функция ОдновременноРегламентныхЗаданий(ПараметрыОдновременноСессий)
	
	ДанныеЖурналаРегистрации 			  = ПараметрыОдновременноСессий.ДанныеЖурналаРегистрации;
	СоответствиеНаименованиеИдентификатор = ПараметрыОдновременноСессий.СоответствиеНаименованиеИдентификатор;
	СоответствиеМетаданныеИдентификатор   = ПараметрыОдновременноСессий.СоответствиеМетаданныеИдентификатор;
	СоответствиеМетаданныеНазвание 		  = ПараметрыОдновременноСессий.СоответствиеМетаданныеНазвание;
	СкрытьРегламентныеЗадания 			  = ПараметрыОдновременноСессий.СкрытьРегламентныеЗадания;
	МинимальнаяПродолжительностьСеансовРегламентныхЗаданий = ПараметрыОдновременноСессий.	
		МинимальнаяПродолжительностьСеансовРегламентныхЗаданий;
										
	ОдновременноСессий = Новый ТаблицаЗначений();
	
	ОдновременноСессий.Колонки.Добавить("ДатаОдновременноРегламентныхЗаданий",
										Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(50)));
	ОдновременноСессий.Колонки.Добавить("ОдновременноРегламентныхЗаданий",
										Новый ОписаниеТипов("Число", Новый КвалификаторыЧисла(10)));
	ОдновременноСессий.Колонки.Добавить("СписокРегламентныхЗаданий");
	
	МассивРегламентныхЗаданий = Новый Массив;
	
	ОдновременноРегламентныхЗаданий	  = 0;
	Счетчик     				  = 0;
	ТекущаяДата 					  = Неопределено;
	СтрокаТаблицы 				  = Неопределено;
	МаксМассивРегламентныхЗаданий = Неопределено;
	
	Для Каждого СтрокаДанныхЖурналаРегистрации Из ДанныеЖурналаРегистрации Цикл 
		Если Не ЗначениеЗаполнено(СтрокаДанныхЖурналаРегистрации.Дата)
			Или Не ЗначениеЗаполнено(СтрокаДанныхЖурналаРегистрации.Метаданные) Тогда
			Продолжить;
		КонецЕсли;
		
		НазваниеИУникальныйИдентификатор = НазваниеИУникальныйИдентификаторСеансаРегламентногоЗадания(
			СтрокаДанныхЖурналаРегистрации, СоответствиеНаименованиеИдентификатор,
			СоответствиеМетаданныеИдентификатор, СоответствиеМетаданныеНазвание);
			
		НазваниеРегламентногоЗадания = НазваниеИУникальныйИдентификатор.НазваниеСеанса;
		УникальныйИдентификаторРегламентногоЗадания = 
			НазваниеИУникальныйИдентификатор.УникальныйИдентификаторРегламентногоЗадания;
		
		Если Не СкрытьРегламентныеЗадания = Неопределено
			И Не ТипЗнч(СкрытьРегламентныеЗадания) = Тип("Строка") Тогда
			ФильтрРегламентныхЗаданий = СкрытьРегламентныеЗадания.НайтиПоЗначению(
				УникальныйИдентификаторРегламентногоЗадания);
			Если Не ФильтрРегламентныхЗаданий = Неопределено Тогда
				Продолжить;
			КонецЕсли;
		ИначеЕсли Не СкрытьРегламентныеЗадания = Неопределено
			И ТипЗнч(СкрытьРегламентныеЗадания) = Тип("Строка") Тогда	
			Если УникальныйИдентификаторРегламентногоЗадания = СкрытьРегламентныеЗадания Тогда
				Продолжить;
			КонецЕсли;
		КонецЕсли;	
		
		ДатаОдновременноРегламентныхЗаданий = НачалоЧаса(СтрокаДанныхЖурналаРегистрации.Дата);
		
		Если ТекущаяДата <> ДатаОдновременноРегламентныхЗаданий Тогда
			Если СтрокаТаблицы <> Неопределено Тогда
				СтрокаТаблицы.ОдновременноРегламентныхЗаданий = ОдновременноРегламентныхЗаданий;
				СтрокаТаблицы.ДатаОдновременноРегламентныхЗаданий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 - %2'"),
					Формат(ТекущаяДата, "ДЛФ=T"),
					Формат(КонецЧаса(ТекущаяДата), "ДЛФ=T"));
				СтрокаТаблицы.СписокРегламентныхЗаданий = МаксМассивРегламентныхЗаданий;
			КонецЕсли;
			СтрокаТаблицы = ОдновременноСессий.Добавить();
			ОдновременноРегламентныхЗаданий = 0;
			Счетчик    = 0;
			МассивРегламентныхЗаданий.Очистить();
			ТекущаяДата = ДатаОдновременноРегламентныхЗаданий;
		КонецЕсли;
		
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Job$_.Start" Тогда
			Счетчик = Счетчик + 1;
			МассивРегламентныхЗаданий.Добавить(НазваниеРегламентногоЗадания);
			МассивРегламентныхЗаданий.Добавить(СтрокаДанныхЖурналаРегистрации.Сеанс);
			МассивРегламентныхЗаданий.Добавить(СтрокаДанныхЖурналаРегистрации.Дата);
		Иначе
			ИндексРегламентногоЗадания = МассивРегламентныхЗаданий.Найти(НазваниеРегламентногоЗадания);
			Если ИндексРегламентногоЗадания = Неопределено Тогда 
				Продолжить;
			КонецЕсли;
			
			Если ЗначениеЗаполнено(МаксМассивРегламентныхЗаданий) Тогда
				ИндексСтрокиМассива = МаксМассивРегламентныхЗаданий.Найти(НазваниеРегламентногоЗадания);
				Если ИндексСтрокиМассива <> Неопределено 
					И МаксМассивРегламентныхЗаданий[ИндексСтрокиМассива+1] = МассивРегламентныхЗаданий[ИндексРегламентногоЗадания+1]
					И СтрокаДанныхЖурналаРегистрации.Дата - МаксМассивРегламентныхЗаданий[ИндексСтрокиМассива+2] <
						МинимальнаяПродолжительностьСеансовРегламентныхЗаданий Тогда
					МаксМассивРегламентныхЗаданий.Удалить(ИндексСтрокиМассива);
					МаксМассивРегламентныхЗаданий.Удалить(ИндексСтрокиМассива);
					МаксМассивРегламентныхЗаданий.Удалить(ИндексСтрокиМассива);
					ОдновременноРегламентныхЗаданий = ОдновременноРегламентныхЗаданий - 1;
				КонецЕсли;
			КонецЕсли;    						
			МассивРегламентныхЗаданий.Удалить(ИндексРегламентногоЗадания);
			МассивРегламентныхЗаданий.Удалить(ИндексРегламентногоЗадания); // Удаляем значение сеанса
			МассивРегламентныхЗаданий.Удалить(ИндексРегламентногоЗадания); // Удаляем значение даты
			Счетчик = Счетчик - 1;
		КонецЕсли;
		
		Счетчик = Макс(Счетчик, 0);
		Если Счетчик > ОдновременноРегламентныхЗаданий Тогда
			МаксМассивРегламентныхЗаданий = Новый Массив;
			Для Каждого Элемент Из МассивРегламентныхЗаданий Цикл
				МаксМассивРегламентныхЗаданий.Добавить(Элемент);
			КонецЦикла;
		КонецЕсли;
		ОдновременноРегламентныхЗаданий = Макс(ОдновременноРегламентныхЗаданий, Счетчик);
	КонецЦикла;
		
	Если ОдновременноРегламентныхЗаданий <> 0 Тогда
		СтрокаТаблицы.ОдновременноРегламентныхЗаданий  = ОдновременноРегламентныхЗаданий;
		СтрокаТаблицы.ДатаОдновременноРегламентныхЗаданий = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 - %2'"),
			Формат(ТекущаяДата, "ДЛФ=T"),
			Формат(КонецЧаса(ТекущаяДата), "ДЛФ=T"));
		СтрокаТаблицы.СписокРегламентныхЗаданий = МаксМассивРегламентныхЗаданий;
	КонецЕсли;
	
	Возврат ОдновременноСессий;
КонецФункции

Функция СеансыРегламентныхЗаданий(ПараметрыСеансыРегламентныхЗаданий)

	ДанныеЖурналаРегистрации = ПараметрыСеансыРегламентныхЗаданий.ДанныеЖурналаРегистрации;
	СоответствиеНаименованиеИдентификатор = ПараметрыСеансыРегламентныхЗаданий.СоответствиеНаименованиеИдентификатор;
	СоответствиеМетаданныеИдентификатор = ПараметрыСеансыРегламентныхЗаданий.СоответствиеМетаданныеИдентификатор;
	СоответствиеМетаданныеНазвание = ПараметрыСеансыРегламентныхЗаданий.СоответствиеМетаданныеНазвание;
	СкрытьРегламентныеЗадания = ПараметрыСеансыРегламентныхЗаданий.СкрытьРегламентныеЗадания;
	ОтображатьФоновыеЗадания = ПараметрыСеансыРегламентныхЗаданий.ОтображатьФоновыеЗадания;  
	
	ТаблицаСеансыРегламентныхЗаданий = Новый ТаблицаЗначений();
	ТаблицаСеансыРегламентныхЗаданий.Колонки.Добавить("ДатаЗапускаЗадания",Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя)));
	ТаблицаСеансыРегламентныхЗаданий.Колонки.Добавить("ДатаОкончанияЗадания",Новый ОписаниеТипов("Дата", , , Новый КвалификаторыДаты(ЧастиДаты.ДатаВремя)));
    ТаблицаСеансыРегламентныхЗаданий.Колонки.Добавить("НазваниеСобытия",Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100)));
	ТаблицаСеансыРегламентныхЗаданий.Колонки.Добавить("СобытиеМетаданные",Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100)));
	ТаблицаСеансыРегламентныхЗаданий.Колонки.Добавить("Сеанс",Новый ОписаниеТипов("Число", 	Новый КвалификаторыЧисла(10)));
	
	КоличествоЗапусков = Новый ТаблицаЗначений();
	КоличествоЗапусков.Колонки.Добавить("НазваниеСобытия",Новый ОписаниеТипов("Строка", , Новый КвалификаторыСтроки(100)));
	КоличествоЗапусков.Колонки.Добавить("Запусков",Новый ОписаниеТипов("Число", 	Новый КвалификаторыЧисла(10)));
	КоличествоЗапусков.Колонки.Добавить("Отменено",Новый ОписаниеТипов("Число", 	Новый КвалификаторыЧисла(10)));
	КоличествоЗапусков.Колонки.Добавить("ОшибкаВыполнения",Новый ОписаниеТипов("Число", 	Новый КвалификаторыЧисла(10))); 	
	
	СтрокаРегламентныеЗадания = Неопределено;
	НазваниеСобытия			  = Неопределено;
	ДатаОкончанияЗадания	  = Неопределено;
	ДатаЗапускаЗадания		  = Неопределено;
	СобытиеМетаданные		  = Неопределено;
	Запусков				  = 0;
	ТекущееСобытие			  = Неопределено;
	СтрокаКоличествоЗапусков  = Неопределено;
	ТекущийСеанс			  = 0;
	Отменено				  = 0;
	ОшибкаВыполнения		  = 0;
	
	Для Каждого СтрокаДанныхЖурналаРегистрации Из ДанныеЖурналаРегистрации Цикл
		Если Не ЗначениеЗаполнено(СтрокаДанныхЖурналаРегистрации.Метаданные)
			И ОтображатьФоновыеЗадания = Ложь Тогда
			Продолжить;
		КонецЕсли;
		
		НазваниеИУникальныйИдентификатор = НазваниеИУникальныйИдентификаторСеансаРегламентногоЗадания(
			СтрокаДанныхЖурналаРегистрации, СоответствиеНаименованиеИдентификатор,
			СоответствиеМетаданныеИдентификатор, СоответствиеМетаданныеНазвание);
			
		НазваниеСобытия = НазваниеИУникальныйИдентификатор.НазваниеСеанса;
		УникальныйИдентификаторРегламентногоЗадания = НазваниеИУникальныйИдентификатор.
														УникальныйИдентификаторРегламентногоЗадания;

		Если Не СкрытьРегламентныеЗадания = Неопределено
			И Не ТипЗнч(СкрытьРегламентныеЗадания) = Тип("Строка") Тогда
			ФильтрРегламентныхЗаданий = СкрытьРегламентныеЗадания.НайтиПоЗначению(
				УникальныйИдентификаторРегламентногоЗадания);
			Если Не ФильтрРегламентныхЗаданий = Неопределено Тогда
				Продолжить;
			КонецЕсли;
		ИначеЕсли Не СкрытьРегламентныеЗадания = Неопределено
			И ТипЗнч(СкрытьРегламентныеЗадания) = Тип("Строка") Тогда	
			Если УникальныйИдентификаторРегламентногоЗадания = СкрытьРегламентныеЗадания Тогда
				Продолжить;
			КонецЕсли;
		КонецЕсли;
	
		Сеанс = СтрокаДанныхЖурналаРегистрации.Сеанс;
		Если ТекущееСобытие = Неопределено Тогда                             
			ТекущееСобытие = НазваниеСобытия;
			Запусков = 0;
		ИначеЕсли ТекущееСобытие <> НазваниеСобытия Тогда
			СтрокаКоличествоЗапусков = КоличествоЗапусков.Добавить();
			СтрокаКоличествоЗапусков.НазваниеСобытия = ТекущееСобытие;
			СтрокаКоличествоЗапусков.Запусков = Запусков;
			СтрокаКоличествоЗапусков.Отменено = Отменено;
			СтрокаКоличествоЗапусков.ОшибкаВыполнения = ОшибкаВыполнения;
			Запусков = 0; 
			Отменено = 0;
			ОшибкаВыполнения = 0;
			ТекущееСобытие = НазваниеСобытия;
		КонецЕсли;  
		
		Если ТекущийСеанс <> Сеанс Тогда
			СтрокаРегламентныеЗадания = ТаблицаСеансыРегламентныхЗаданий.Добавить();
			ДатаЗапускаЗадания = СтрокаДанныхЖурналаРегистрации.Дата;
			СтрокаРегламентныеЗадания.ДатаЗапускаЗадания = ДатаЗапускаЗадания;    
		КонецЕсли;
		
		Если ТекущийСеанс = Сеанс Тогда
			ДатаОкончанияЗадания = СтрокаДанныхЖурналаРегистрации.Дата;
			СобытиеМетаданные = СтрокаДанныхЖурналаРегистрации.Метаданные;
			СтрокаРегламентныеЗадания.НазваниеСобытия = НазваниеСобытия;
			СтрокаРегламентныеЗадания.СобытиеМетаданные = СобытиеМетаданные;
			СтрокаРегламентныеЗадания.ДатаОкончанияЗадания = ДатаОкончанияЗадания;
			СтрокаРегламентныеЗадания.Сеанс = ТекущийСеанс;
		КонецЕсли;
		ТекущийСеанс = Сеанс;
		
		Если СтрокаДанныхЖурналаРегистрации.Событие = "_$Job$_.Cancel" Тогда
			Отменено = Отменено + 1;
		ИначеЕсли СтрокаДанныхЖурналаРегистрации.Событие = "_$Job$_.Fail" Тогда
			ОшибкаВыполнения = ОшибкаВыполнения + 1;
		ИначеЕсли СтрокаДанныхЖурналаРегистрации.Событие = "_$Job$_.Start" Тогда
			Запусков = Запусков + 1
		КонецЕсли;		
	КонецЦикла;
	
	СтрокаКоличествоЗапусков = КоличествоЗапусков.Добавить();
	СтрокаКоличествоЗапусков.НазваниеСобытия = ТекущееСобытие;
	СтрокаКоличествоЗапусков.Запусков = Запусков;
	СтрокаКоличествоЗапусков.Отменено = Отменено;
	СтрокаКоличествоЗапусков.ОшибкаВыполнения = ОшибкаВыполнения;
	
	ТаблицаСеансыРегламентныхЗаданий.Сортировать("СобытиеМетаданные, НазваниеСобытия, ДатаЗапускаЗадания");
	
	Возврат Новый Структура("ТаблицаСеансыРегламентныхЗаданий, КоличествоЗапусков",
					ТаблицаСеансыРегламентныхЗаданий, КоличествоЗапусков);
КонецФункции

// Функция формирования отчета по выбранному регламентному заданию.
// Параметры:
//   Расшифровка - расшифровка регламентного задания.
//
Функция РасшифровкаРегламентногоЗадания(Расшифровка) Экспорт
	Результат = Новый Структура;
	Отчет = Новый ТабличныйДокумент;
	ОтмененоЗаданий = 0;
	ОшибкаВыполнения = 0;
	
	ДатаЗапускаЗадания = Расшифровка.Получить(5);
	ДатаОкончанияЗадания = Расшифровка.Получить(6);
	СписокСеансов = Расшифровка.Получить(7);
	Макет = ПолучитьМакет("РасшифровкаРегламентныхЗаданий");
	
	Область = ОписаниеОбластиМакета(Макет, "Заголовок");
	ДатаНачала = Расшифровка.Получить(11);
	ДатаОкончания = Расшифровка.Получить(12);
	Область.Параметры.ДатаНачала = ДатаНачала;
	Область.Параметры.ДатаОкончания = ДатаОкончания;
	Если Расшифровка.Получить(8) = 0 Тогда
		РежимОтображенияИнтервалов = НСтр("ru = 'Включено отображение интервалов с нулевой продолжительностью'");
	Иначе
		РежимОтображенияИнтервалов = НСтр("ru = 'Отключено отображение интервалов с нулевой продолжительностью'");
	КонецЕсли;
	Область.Параметры.РежимОтображенияСеансов = РежимОтображенияИнтервалов;
	Отчет.Вывести(Область);
	
	Отчет.Вывести(Макет.ПолучитьОбласть("ПустаяСтрока"));
	
	Область = ОписаниеОбластиМакета(Макет, "Таблица");
	Область.Параметры.ТипЗадания = НСтр("ru = 'Регламентное'");
	Область.Параметры.НазваниеСобытия = Расшифровка.Получить(2);
	Область.Параметры.Запусков = Расшифровка.Получить(9);
	ОтмененоЗаданий = Расшифровка.Получить(3);
	ОшибкаВыполнения = Расшифровка.Получить(4);
	Если ОтмененоЗаданий = 0 Тогда
		Область.Параметры.Отменено = "0";
	Иначе
		Область.Параметры.Отменено = ОтмененоЗаданий;
	КонецЕсли;
	Если ОшибкаВыполнения = 0 Тогда 
		Область.Параметры.ОшибкаВыполнения = "0";
	Иначе
		Область.Параметры.ОшибкаВыполнения = ОшибкаВыполнения;
	КонецЕсли;
	ОбщаяПродолжительностьРЗ = Расшифровка.Получить(10);
	ОбщаяПродолжительностьРЗИтог = ПродолжительностьРегламентногоЗадания(ОбщаяПродолжительностьРЗ);
	Область.Параметры.ОбщаяПродолжительностьРЗ = ОбщаяПродолжительностьРЗИтог;
	Отчет.Вывести(Область);
	
	Отчет.Вывести(Макет.ПолучитьОбласть("ПустаяСтрока")); 
	
	Отчет.Вывести(Макет.ПолучитьОбласть("ЗаголовокИнтервалов"));
		
	Отчет.Вывести(Макет.ПолучитьОбласть("ПустаяСтрока"));
	
	Отчет.Вывести(Макет.ПолучитьОбласть("ШапкаТаблицы"));
	
	// Заполняем таблицу интервалов.
	РазмерМассива = ДатаЗапускаЗадания.Количество();
	НомерИнтервала = 1; 	
    Отчет.НачатьГруппуСтрок(, Ложь);
	Для Индекс = 0 По РазмерМассива-1 Цикл
		Область = ОписаниеОбластиМакета(Макет, "ТаблицаИнтервалов");
		НачалоИнтервала = ДатаЗапускаЗадания.Получить(Индекс);
		КонецИнтервала = ДатаОкончанияЗадания.Получить(Индекс);
		ПродолжительностьРЗ = ПродолжительностьРегламентногоЗадания(КонецИнтервала - НачалоИнтервала);
		Область.Параметры.НомерИнтервала = НомерИнтервала;
		Область.Параметры.НачалоИнтервала = Формат(НачалоИнтервала, "ДЛФ=T");
		Область.Параметры.КонецИнтервала = Формат(КонецИнтервала, "ДЛФ=T");
		Область.Параметры.ПродолжительностьРЗ = ПродолжительностьРЗ;
		Область.Параметры.Сеанс = СписокСеансов.Получить(Индекс);
		Область.Параметры.РасшифровкаИнтервала = Новый Массив;
		Область.Параметры.РасшифровкаИнтервала.Добавить(НачалоИнтервала);
		Область.Параметры.РасшифровкаИнтервала.Добавить(КонецИнтервала);
		Область.Параметры.РасшифровкаИнтервала.Добавить(СписокСеансов.Получить(Индекс));
		Отчет.Вывести(Область);
		НомерИнтервала = НомерИнтервала + 1;
	КонецЦикла;
	Отчет.ЗакончитьГруппуСтрок();
	
	Результат.Вставить("Отчет", Отчет);
	Возврат Результат;
КонецФункции

// Процедура для установки цвета интервалов и фона диаграммы ганта.
//
// Параметры:
//   ДатаНачала - день, за который производится построение диаграммы.
//   ДиаграммаГанта - ДиаграммаГанта, Тип - РисунокТабличногоДокумента.
//   ОдновременноСессий - ТаблицаЗначений - с данными по количеству одновременно работавших
// 		регламентных заданий в течении дня.
//   ЦветаФона - массив цветов для интервалов фона.
//
Процедура ЦветаДиаграммыГанта(ДатаНачала, ДиаграммаГанта, ОдновременноСессий, ЦветаФона)
	// Добавляем цвета интервалов фона.
	ТекущееКоличествоСессий = 0;
	ИндексЦвета = 3;
	Для Каждого СтрокаОдновременноСессий Из ОдновременноСессий Цикл
		Если СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий = 1 Тогда
			Продолжить
		КонецЕсли;
		СтрокаДаты = Лев(СтрокаОдновременноСессий.ДатаОдновременноРегламентныхЗаданий, 8);
		ДатаНачалоИФ =  Дата(Формат(ДатаНачала,"ДЛФ=D") + " " + СтрокаДаты);
		ДатаКонецИФ = КонецЧаса(ДатаНачалоИФ);
		ИнтервалДГ = ДиаграммаГанта.ИнтервалыФона.Добавить(ДатаНачалоИФ, ДатаКонецИФ);
		Если ТекущееКоличествоСессий <> 0 
			И ТекущееКоличествоСессий <> СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий 
			И ИндексЦвета <> 0 Тогда
			ИндексЦвета = ИндексЦвета - 1;
		КонецЕсли;
		ЦветФона = ЦветаФона.Получить(ИндексЦвета);
		ИнтервалДГ.Цвет = ЦветФона;
		
		ТекущееКоличествоСессий = СтрокаОдновременноСессий.ОдновременноРегламентныхЗаданий;
	КонецЦикла;
КонецПроцедуры

// Процедура формирования шкалы времени диаграммы ганта.
//
// Параметры:
//   ДиаграммаГанта - ДиаграммаГанта, Тип - РисунокТабличногоДокумента.
//
Процедура ШкалаВремениДиаграммыГанта(ДиаграммаГанта, ПериодАнализа)
	ЭлементыШкалыВремени = ДиаграммаГанта.ОбластьПостроения.ШкалаВремени.Элементы;
	
	ПервыйЭлемент = ЭлементыШкалыВремени[0];
	Для Индекс = 1 По ЭлементыШкалыВремени.Количество()-1 Цикл
		ЭлементыШкалыВремени.Удалить(ЭлементыШкалыВремени[1]);
	КонецЦикла; 
		
	ПервыйЭлемент.Единица = ТипЕдиницыШкалыВремени.День;
	ПервыйЭлемент.ЛинииДелений = Новый Линия(ТипЛинииДиаграммы.Сплошная, 1);
	ПервыйЭлемент.ФорматДня =  ФорматДняШкалыВремени.ДеньМесяца;
	
	Элемент = ЭлементыШкалыВремени.Добавить();
	Элемент.Единица = ТипЕдиницыШкалыВремени.Час;
	Элемент.ЛинииДелений = Новый Линия(ТипЛинииДиаграммы.Точечная, 1);
	
	Если ПериодАнализа <= 3600 Тогда
		Элемент = ЭлементыШкалыВремени.Добавить();
		Элемент.Единица = ТипЕдиницыШкалыВремени.Минута;
		Элемент.ЛинииДелений = Новый Линия(ТипЛинииДиаграммы.Точечная, 1);
	КонецЕсли;
КонецПроцедуры

Функция ПродолжительностьРегламентногоЗадания(ПродолжительностьРЗ)
	Если ПродолжительностьРЗ = 0 Тогда
		ОбщаяПродолжительностьРЗ = "0";
	ИначеЕсли ПродолжительностьРЗ <= 60 Тогда
		ОбщаяПродолжительностьРЗ = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 сек'"), ПродолжительностьРЗ);
	ИначеЕсли 60 < ПродолжительностьРЗ <= 3600 Тогда
		ПродолжительностьМинуты  = Формат(ПродолжительностьРЗ/60, "ЧДЦ=0");
		ПродолжительностьСекунды = Формат((Формат(ПродолжительностьРЗ/60, "ЧДЦ=2")
			- Цел(ПродолжительностьРЗ/60)) * 60, "ЧДЦ=0");
		ОбщаяПродолжительностьРЗ = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 мин %2 сек'"), ПродолжительностьМинуты, ПродолжительностьСекунды);
	ИначеЕсли ПродолжительностьРЗ > 3600 Тогда
		ПродолжительностьЧасы    = Формат(ПродолжительностьРЗ/60/60, "ЧДЦ=0");
		ПродолжительностьМинуты  = (Формат(ПродолжительностьРЗ/60/60, "ЧДЦ=2") - Цел(ПродолжительностьРЗ/60/60))*60;
		ПродолжительностьМинуты  = Формат(ПродолжительностьМинуты, "ЧДЦ=0");
		ОбщаяПродолжительностьРЗ = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = '%1 ч %2 мин'"), ПродолжительностьЧасы, ПродолжительностьМинуты);
	КонецЕсли;
	
	Возврат ОбщаяПродолжительностьРЗ;
КонецФункции

Функция МетаданныеРегламентногоЗадания(ДанныеРегламентногоЗадания)
	Если ДанныеРегламентногоЗадания <> "" Тогда
		Возврат Метаданные.РегламентныеЗадания.Найти(
			СтрЗаменить(ДанныеРегламентногоЗадания, "РегламентноеЗадание." , ""));
	КонецЕсли;
КонецФункции

Функция НазваниеИУникальныйИдентификаторСеансаРегламентногоЗадания(СтрокаДанныхЖурналаРегистрации,
			СоответствиеНаименованиеИдентификатор, СоответствиеМетаданныеИдентификатор, СоответствиеМетаданныеНазвание)
	Если Не СтрокаДанныхЖурналаРегистрации.Данные = "" Тогда
		УникальныйИдентификаторРегламентногоЗадания = СоответствиеНаименованиеИдентификатор[
														СтрокаДанныхЖурналаРегистрации.Данные];
		НазваниеСеанса = СтрокаДанныхЖурналаРегистрации.Данные;
	Иначе 
		УникальныйИдентификаторРегламентногоЗадания = СоответствиеМетаданныеИдентификатор[
			МетаданныеРегламентногоЗадания(СтрокаДанныхЖурналаРегистрации.Метаданные)];
		НазваниеСеанса = СоответствиеМетаданныеНазвание[МетаданныеРегламентногоЗадания(
														СтрокаДанныхЖурналаРегистрации.Метаданные)];
	КонецЕсли;
													
	Возврат Новый Структура("НазваниеСеанса, УникальныйИдентификаторРегламентногоЗадания",
								НазваниеСеанса, УникальныйИдентификаторРегламентногоЗадания)
КонецФункции

// Параметры:
//  Макет - ТабличныйДокумент
//  ИмяОбласти - Строка
//
// Возвращаемое значение:
//  ТабличныйДокумент:
//    * Параметры - ПараметрыМакетаТабличногоДокумента:
//        ** ДатаНачала - Дата
//        ** ДатаОкончания - Дата
//        ** РежимОтображенияИнтервалов - Строка
//        ** СписокРегламентныхЗаданий - Строка
//        ** РасшифровкаЗадания - Массив из Строка
//                              - Дата
//        ** РасшифровкаИнтервала - Массив из Строка
//
Функция ОписаниеОбластиМакета(Макет, ИмяОбласти)
	
	Возврат Макет.ПолучитьОбласть(ИмяОбласти);
	
КонецФункции

////////////////////////////////////////////////////////////////////////////////
// Контроль журнала регистрации.

// Функция, формирующая отчет по зарегистрированным в журнале регистрации ошибкам.
//
// Параметры:
//   ДанныеЖурналаРегистрации - ТаблицаЗначений - выгруженная таблица из журнала регистраций.
//
// Должны присутствовать следующие колонки: Дата, ИмяПользователя, ПредставлениеПриложения,
//                                          ПредставлениеСобытия, Комментарий, Уровень.
//
Функция СформироватьОтчетКонтрольЖурналаРегистрации(ДатаНачала, ДатаОкончания, СмещениеВремениСервера) Экспорт
	
	Результат = Новый Структура; 	
	Отчет = Новый ТабличныйДокумент; 	
	Макет = ПолучитьМакет("МакетОтчетаПоОшибкамВЖурналеРегистрации");
	ДанныеЖурналаРегистрации = ИнформацияПоОшибкамЖурналаРегистрации(ДатаНачала, ДатаОкончания, СмещениеВремениСервера);
	КоличествоЗаписейЖурналаРегистрации = ДанныеЖурналаРегистрации.Количество();
	
	ОтчетПустой = (КоличествоЗаписейЖурналаРегистрации = 0); // Проверяем заполнение отчета.
		
	///////////////////////////////////////////////////////////////////////////////
	// Блок предварительной подготовки данных.
	//
	
	СверткаПоКомментариям = ДанныеЖурналаРегистрации.Скопировать();
	СверткаПоКомментариям.Колонки.Добавить("ИтогПоКомментарию");
	СверткаПоКомментариям.ЗаполнитьЗначения(1, "ИтогПоКомментарию");
	СверткаПоКомментариям.Свернуть("Уровень, Комментарий, Событие, ПредставлениеСобытия", "ИтогПоКомментарию");
	
	МассивСтрок_УровеньОшибка = СверткаПоКомментариям.НайтиСтроки(
									Новый Структура("Уровень", УровеньЖурналаРегистрации.Ошибка));
	
	МассивСтрок_УровеньПредупреждение = СверткаПоКомментариям.НайтиСтроки(
									Новый Структура("Уровень", УровеньЖурналаРегистрации.Предупреждение));
	
	Свертка_Ошибки         = СверткаПоКомментариям.Скопировать(МассивСтрок_УровеньОшибка);
	Свертка_Ошибки.Сортировать("ИтогПоКомментарию Убыв");
	Свертка_Предупреждения = СверткаПоКомментариям.Скопировать(МассивСтрок_УровеньПредупреждение);
	Свертка_Предупреждения.Сортировать("ИтогПоКомментарию Убыв");
	
	///////////////////////////////////////////////////////////////////////////////
	// Блок формирования самого отчета.
	//
	
	Область = Макет.ПолучитьОбласть("ШапкаОтчета");
	Область.Параметры.ПериодВыборкиНачало    = ДатаНачала;
	Область.Параметры.ПериодВыборкиОкончание = ДатаОкончания;
	Область.Параметры.ПредставлениеИнформационнойБазы = ПредставлениеИнформационнойБазы();
	Отчет.Вывести(Область);
	
	РезультатКомпоновкиТЧ = СформироватьТабличнуюЧасть(Макет, ДанныеЖурналаРегистрации, Свертка_Ошибки);
	
	Отчет.Вывести(Макет.ПолучитьОбласть("ПустаяСтрока"));
	Область = Макет.ПолучитьОбласть("ЗаголовокБлокаОшибки");
	Область.Параметры.ЧислоОшибок = Строка(РезультатКомпоновкиТЧ.Итог);
	Отчет.Вывести(Область);
	
	Если РезультатКомпоновкиТЧ.Итог > 0 Тогда
		Отчет.Вывести(РезультатКомпоновкиТЧ.ТабличнаяЧасть);
	КонецЕсли;
	
	Результат.Вставить("ИтогПоОшибкам", РезультатКомпоновкиТЧ.Итог); 	
	РезультатКомпоновкиТЧ = СформироватьТабличнуюЧасть(Макет, ДанныеЖурналаРегистрации, Свертка_Предупреждения);
	
	Отчет.Вывести(Макет.ПолучитьОбласть("ПустаяСтрока"));
	Область = Макет.ПолучитьОбласть("ЗаголовокБлокаПредупреждения");
	Область.Параметры.ЧислоПредупреждений = РезультатКомпоновкиТЧ.Итог;
	Отчет.Вывести(Область);
	
	Если РезультатКомпоновкиТЧ.Итог > 0 Тогда
		Отчет.Вывести(РезультатКомпоновкиТЧ.ТабличнаяЧасть);
	КонецЕсли;
	
	Результат.Вставить("ИтогПоПредупреждениям", РезультатКомпоновкиТЧ.Итог);	
	Отчет.ОтображатьСетку = Ложь; 	
	Результат.Вставить("Отчет", Отчет); 
	Результат.Вставить("ОтчетПустой", ОтчетПустой);
	Возврат Результат;
	
КонецФункции

// Получить представление физического места размещения информационной базы для отображения администратору.
//
// Возвращаемое значение:
//   Строка - представление информационной базы.
//
// Пример:
// - для ИБ в файлом режиме: \\FileServer\1c_ib\
// - для ИБ в серверном режиме: ServerName:1111 / information_base_name.
//
Функция ПредставлениеИнформационнойБазы()
	
	СтрокаСоединенияСБД = СтрокаСоединенияИнформационнойБазы();
	
	Если ОбщегоНазначения.ИнформационнаяБазаФайловая(СтрокаСоединенияСБД) Тогда
		Возврат Сред(СтрокаСоединенияСБД, 6, СтрДлина(СтрокаСоединенияСБД) - 6);
	КонецЕсли;
		
	// Прибавить к имени сервера имя пути информационной базы.
	ПозицияПоиска = СтрНайти(ВРег(СтрокаСоединенияСБД), "SRVR=");
	Если ПозицияПоиска <> 1 Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	ПозицияТочкиСЗапятой = СтрНайти(СтрокаСоединенияСБД, ";");
	НачальнаяПозицияКопирования = 6 + 1;
	КонечнаяПозицияКопирования = ПозицияТочкиСЗапятой - 2; 
	
	ИмяСервера = Сред(СтрокаСоединенияСБД, НачальнаяПозицияКопирования, КонечнаяПозицияКопирования - НачальнаяПозицияКопирования + 1);
	
	СтрокаСоединенияСБД = Сред(СтрокаСоединенияСБД, ПозицияТочкиСЗапятой + 1);
	
	// Позиция имени сервера
	ПозицияПоиска = СтрНайти(ВРег(СтрокаСоединенияСБД), "REF=");
	Если ПозицияПоиска <> 1 Тогда
		Возврат Неопределено;
	КонецЕсли;
	
	НачальнаяПозицияКопирования = 6;
	ПозицияТочкиСЗапятой = СтрНайти(СтрокаСоединенияСБД, ";");
	КонечнаяПозицияКопирования = ПозицияТочкиСЗапятой - 2; 
	
	ИмяИБНаСервере = Сред(СтрокаСоединенияСБД, НачальнаяПозицияКопирования, КонечнаяПозицияКопирования - НачальнаяПозицияКопирования + 1);
	ПутьКБД = ИмяСервера + "/ " + ИмяИБНаСервере;
	Возврат ПутьКБД;
	
КонецФункции

// Функция получает информацию по ошибкам в журнале регистрации по переданному периоду.
//
// Параметры:
//   ДатаНачала    - Дата - начала периода, по которому будет собираться информация.
//   ДатаОкончания - Дата - окончание периода, по которому будет собираться информация.
//
// Возвращаемое значение
//   ТаблицаЗначений - записи из журнала регистрации в соответствии с фильтром:
//                    УровеньЖурналаРегистрации - УровеньЖурналаРегистрации.Ошибка
//                    Начало и Окончание периода - из параметров.
//
Функция ИнформацияПоОшибкамЖурналаРегистрации(Знач ДатаНачала, Знач ДатаОкончания, СмещениеВремениСервера)
	
	ДанныеЖурналаРегистрации = Новый ТаблицаЗначений;
	
	УровниРегистрацииОшибок = Новый Массив;
	УровниРегистрацииОшибок.Добавить(УровеньЖурналаРегистрации.Ошибка);
	УровниРегистрацииОшибок.Добавить(УровеньЖурналаРегистрации.Предупреждение);
	
	ДатаНачала = ДатаНачала + СмещениеВремениСервера;
	ДатаОкончания = ДатаОкончания + СмещениеВремениСервера;
	
	УстановитьПривилегированныйРежим(Истина);
	ВыгрузитьЖурналРегистрации(ДанныеЖурналаРегистрации,
							   Новый Структура("Уровень, ДатаНачала, ДатаОкончания",
											   УровниРегистрацииОшибок,
											   ДатаНачала,
											   ДатаОкончания));
	УстановитьПривилегированныйРежим(Ложь);
	
	Если СмещениеВремениСервера <> 0 Тогда
		Для Каждого СтрокаТаблицы Из ДанныеЖурналаРегистрации Цикл
			СтрокаТаблицы.Дата = СтрокаТаблицы.Дата - СмещениеВремениСервера;
		КонецЦикла;
	КонецЕсли;
	
	Возврат ДанныеЖурналаРегистрации;
	
КонецФункции

// Добавляет в отчет табличную часть по ошибкам. Ошибки выводятся сгруппированными
// по комментарию.
//
// Параметры:
//   Макет  - ТабличныйДокумент - источник форматированных областей, которые будут
//                              использоваться при формировании отчета.
//   ДанныеЖурналаРегистрации   - ТаблицаЗначений - данные по ошибкам и предупреждениям
//                              из журнала регистрации "как есть".
//   СвернутыеДанные - ТаблицаЗначений - свернутая по комментариям информация по их количеству.
//
Функция СформироватьТабличнуюЧасть(Макет, ДанныеЖурналаРегистрации, СвернутыеДанные)
	
	Отчет = Новый ТабличныйДокумент;	
	Итог = 0;
	
	Если СвернутыеДанные.Количество() > 0 Тогда
		Отчет.Вывести(Макет.ПолучитьОбласть("ПустаяСтрока"));
		
		Для Каждого Запись Из СвернутыеДанные Цикл
			Итог = Итог + Запись.ИтогПоКомментарию;
			МассивСтрок = ДанныеЖурналаРегистрации.НайтиСтроки(
				Новый Структура("Уровень, Комментарий",
					УровеньЖурналаРегистрации.Ошибка,
					Запись.Комментарий));
			
			Область = Макет.ПолучитьОбласть("ТелоТабличнойЧастиШапка");
			Область.Параметры.Заполнить(Запись);
			Отчет.Вывести(Область);
			
			Отчет.НачатьГруппуСтрок(, Ложь);
			Для Каждого Строка Из МассивСтрок Цикл
				Область = Макет.ПолучитьОбласть("ТелоТабличнойЧастиДетализация");
				Область.Параметры.Заполнить(Строка);
				Отчет.Вывести(Область);
			КонецЦикла;
			Отчет.ЗакончитьГруппуСтрок();
			Отчет.Вывести(Макет.ПолучитьОбласть("ПустаяСтрока"));
		КонецЦикла;
	КонецЕсли;
	
	Результат = Новый Структура("ТабличнаяЧасть, Итог", Отчет, Итог);
	
	Возврат Результат;
	
КонецФункции

#КонецОбласти

#КонецЕсли